package ex1;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Sheet2 Exercise1 
 * 
 * @author Dennis Meyer, Sebastian Brodehl, Sebastian Schappert
 * @version 0.0.0.1
 *
 */
public class CurriculumVitae implements java.io.Serializable{
	
	/**
	 * serialVersionUID for further deserialization purposes
	 */
	private static final long serialVersionUID = 1L;
	
	private CVTheme theme;
	private CVData cvData;
	private CVDataIO cvDataIO;
	private String filePath;
	private final String REGEXGERMANPHONENUMBER = "^(((((((00|\\+)[0-9][0-9][ \\-/]?)|0)[1-9][0-9]{1,4})[ \\-/]?)|((((00|\\+)49\\()|\\(0)[1-9][0-9]{1,4}\\)[ \\-/]?))[0-9]{1,7}([ \\-/]?[0-9]{1,5})?)$";
	private final String REGEXEMAIL = "^([a-zA-Z0-9_\\-\\.]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)$";
	
	public CVTheme getTheme() {
		return theme;
	}

	public void setTheme(CVTheme theme) {
		this.theme = theme;
	}

	public CVData getCvData() {
		return cvData;
	}

	public void setCvData(CVData cvData) {
		this.cvData = cvData;
	}

	public CVDataIO getCvDataIO() {
		return cvDataIO;
	}

	public void setCvDataIO(CVDataIO cvDataIO) {
		this.cvDataIO = cvDataIO;
	}

	public String getFilePath() {
		return filePath;
	}

	public void setFilePath(String filePath) {
		this.filePath = filePath;
	}
	
	/**
	 * Konstruktor
	 */
	public CurriculumVitae(String filePath)
	{
		this.setFilePath(filePath);
		this.setCVTheme(Color.blue, Style.classic);
		this.cvData = new CVData();
	}
	
	/**
	 * Saves the actual Data in a file.
	 * 
	 * @param filepath 
	 * @return true if file saved.
	 */
	public boolean saveCV(String filePath) 
	{
		if(this.getCvData() == null)
			return false;
		this.setCvDataIO( new CVDataIO() );
		this.getCvDataIO().writeCVData(this.getCvData(), filePath);
		System.out.println("File successfully saved: " +filePath);
		return true;
	}

	/**
	 * Loads CVData into the CV object
	 * 
	 * @param CVDataIO object with should be loaded.
	 * @return true if successfully loaded.
	 */
	public boolean loadCV(String filePath)
	{
		CVData tmp = new CVDataIO().readCVFile(filePath);
		if(tmp == null)
			return false;
		this.setCvData(tmp);
		System.out.println("File successfully loaded: " +filePath);
		return true;
	}

	/**
	 * Sets the Theme of the CV
	 * 
	 * @param Color object, Style object
	 * @return void
	 */
	private void setCVTheme(Color color, Style style)
	{
		this.setTheme(new CVTheme(color, style));
	}
	
	/**
	 * Convert Umlaut in Latex Umlaut.
	 * 
	 * @param textWithUmlaut the text with umlaut
	 * @return equal text without umlaut.
	 */
	private String convertUmlaut(String textWithUmlaut){
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"a");
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"o");
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"u");
		
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"A");
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"O");
		textWithUmlaut = textWithUmlaut.replace("��", "\\\"U");
		
		textWithUmlaut = textWithUmlaut.replace("��", "\\ss ");

		return textWithUmlaut;
	}

	/**
	 * Create a string wtih personaldata and give it back.
	 * 
	 * @param givenName
	 * @param lastName
	 * @param picturePath
	 * @return a formatted text, wich latex can read.
	 */
	private String createPersonalData(String givenName, String lastName, String picturePath){
		
		StringBuilder personalData = new StringBuilder();
		
		try {
			// Check given data
			if (givenName.equals("") || lastName.equals(""))
				throw new MissingNameException("MissingName");
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			personalData.append("Error: " + ex.getMessage());
		};
		
		// add comment
		personalData.append("%given personal data" + System.getProperty("line.separator") );
		
		// Create GivenName
		personalData.append("\\firstname{" + givenName + "}" + System.getProperty("line.separator") );
		
		// Create LastName
		personalData.append("\\familyname{" + lastName + "}" + System.getProperty("line.separator") );

		File tempPictureFile = new File(picturePath);
		// Create PicturePath 
		if (tempPictureFile.exists()) 
			personalData.append("\\photo[96pt]{" + picturePath + "}" + System.getProperty("line.separator") );
				
		return this.convertUmlaut(personalData.toString());
	}

	/**
	 * Create a mobileline in latex format.
	 * 
	 * @param mobileNumber
	 * @return the formatted mobileLine.
	 */
	private String createMobileLine(String mobileNumber) {
		try {
			// Check given string
			if (!mobileNumber.matches(REGEXGERMANPHONENUMBER))
				throw new InvalidMobileNumberException("InvalidMobileNumber");
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return ("Error: " + ex.getMessage());
		}
		
		return this.createCVLine("\\mobilesymbol", mobileNumber);
	}

	/**
	 * Create a emailline in latex format.
	 * 
	 * @param emailLine
	 * @return the formatted emailline.
	 */
	private String createEmailLine(String emailLine) {	
		try {
			// Check given string
			if (!emailLine.matches(REGEXEMAIL))
				throw new InvalidEmailException("InvalidEmail");
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return ("Error: " + ex.getMessage());
		}
		
		return this.createCVLine("\\emailsymbol", "\\href{mailto:" + emailLine + "}{" + emailLine +"}");
	}

	/**
	 * Create a cvline for latex.
	 * 
	 * @param first 
	 * @param second
	 * @return the cvline
	 */
	private String createCVLine(String first, String second) {
		try {
			// Check given string
			if (second.equals(""))
				throw new InvalidCVLineException("InvalidCVLine");
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return ("Error: " + ex.getMessage());
		}
		
		StringBuilder createCVLineBuilder = new StringBuilder();
		createCVLineBuilder.append("\\cvline{" + this.convertUmlaut(first) + "}{" + this.convertUmlaut(second) + "}" + System.getProperty("line.separator") );
		
		return createCVLineBuilder.toString();
	}

	/**
	 * Create a cventry for latex.
	 * 
	 * @param cvEntryArray
	 * @return the cventry
	 */
	private String createCVEntry(String[] cvEntryArray) {
		try {
			// Check given string
			if (cvEntryArray[0].equals("") || cvEntryArray[1].equals(""))	
				throw new InvalidCVEntryException("InvalidCVEntry");
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return ("Error: " + ex.getMessage());
		}
		
		StringBuilder createCVEntryBuilder = new StringBuilder();
		createCVEntryBuilder.append("\\cventry{"+cvEntryArray[0]+"} {"+cvEntryArray[1]+"}{"+cvEntryArray[2]+"}{"+cvEntryArray[3]+"}{"+cvEntryArray[4]+"}{"+cvEntryArray[5]+"}"+System.getProperty("line.separator"));
	
		return this.convertUmlaut(createCVEntryBuilder.toString());
	}

	/**
	 * Create a tex File
	 * 
	 * @param fullFilePath Path to tex File
	 */
	public void writeCV() {
		File tempFile = new File(this.getFilePath());
		
		try {
			// Check if File exist
			if (!tempFile.exists())
				tempFile.createNewFile();
			
			// Check if file is readable
			if(!tempFile.canRead())
				throw new Exception("Can't read the file!");
			
			// Check if extension equls .tex
			if (!tempFile.getAbsolutePath().endsWith(".tex"))
				throw new Exception("File does not ends with .tex");
				
			// Check if file is writable
			if (!tempFile.canWrite())
				throw new Exception("File is read-only!");
			
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			this.printErrorToLogFile("Error: " + ex.getMessage());
		}
		
		StringBuilder texFile = new StringBuilder();
		texFile.append("%use class moderncv");
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\documentclass[11pt,a4paper] {moderncv}");
		texFile.append(System.getProperty("line.separator") + System.getProperty("line.separator"));
		texFile.append("%language package");
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\usepackage[german]{babel}");
		texFile.append(System.getProperty("line.separator") + System.getProperty("line.separator"));
		texFile.append("%choosen theme");
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\moderncvtheme[" + this.getTheme().getClass() + "]{" + this.getTheme().getStyle() + "}");
		texFile.append(System.getProperty("line.separator") + System.getProperty("line.separator"));
		
		texFile.append(this.createPersonalData(this.cvData.getGivenName(), this.cvData.getLastName(), this.cvData.getPhotoPath()));
		texFile.append(System.getProperty("line.separator"));
		texFile.append("%create a document");
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\begin{document}");
		texFile.append(System.getProperty("line.separator") + System.getProperty("line.separator"));
		
		texFile.append("%create title based on personal data");
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\maketitle");
		texFile.append(System.getProperty("line.separator"));
		
		// Section contact data
		texFile.append(this.createNewSection("Kontaktdaten", "section for contact data"));
		texFile.append(this.createCVLine("", this.convertUmlaut(this.cvData.getStreet())));
		texFile.append(this.createCVLine("", this.convertUmlaut(this.cvData.getCourse())));
		texFile.append(this.createMobileLine(this.convertUmlaut(this.cvData.getMobileNumber())));
		texFile.append(this.createEmailLine(this.convertUmlaut(this.cvData.getEmail())));
		
		//Sections
		for (CVSection section : cvData.getSections()) 
		{
			texFile.append(this.createNewSection(convertUmlaut(section.getName()), convertUmlaut(section.getComment())));
			for (int i = 0; i < section.getCvEntrys().size(); i++) 
			{
				texFile.append(this.createCVEntry(section.getCvEntrys().get(i)));
			}
			for (int i = 0; i < section.getCvLines().size(); i++) 
			{
				texFile.append(this.createCVLine(section.getCvLines().get(i)[0], section.getCvLines().get(i)[1]));
			}
		}
		
		texFile.append(System.getProperty("line.separator"));
		texFile.append("\\end{document}");
		
		try 
		{
			// Check if File exist
			if (!tempFile.exists())
				throw new FileNotFoundException("File does not exist!");
			
			RandomAccessFile raf = new RandomAccessFile(this.getFilePath(), "rw");
			raf.seek(raf.length());
			raf.write(texFile.toString().getBytes());
			raf.write(System.getProperty("line.separator").getBytes());
			raf.close();
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return;
		}
		
		System.out.println("File successfully saved: " +this.getFilePath());
	}
	// method to create a new latex section
	private String createNewSection(String sectionName, String comment) {
		StringBuilder section = new  StringBuilder();
		section.append(System.getProperty("line.separator"));
		section.append("%" + comment);
		section.append(System.getProperty("line.separator"));
		section.append("\\section{"+sectionName+"}");
		section.append(System.getProperty("line.separator"));
		return section.toString();
	}
	// create log file
	private void printErrorToLogFile(String errorMessage) {
		File tempFile = new File(this.getFilePath());
		
		try {
			// Check if File exist
			if (!tempFile.exists())
				throw new FileNotFoundException("File does not exist!");
			
			RandomAccessFile raf = new RandomAccessFile(this.getFilePath(), "rw");
			raf.seek(raf.length());
			raf.write(errorMessage.getBytes());
			raf.write(System.getProperty("line.separator").getBytes());
			raf.close();
			
			System.exit(0);
		} catch (Exception ex) {
			System.out.println("Error: " + ex.getMessage());
			return;
		}
	}
	// create method to compress cv and picture file
	public void compressCV(String format) {
		try {//check format string
			if (!(format.equals("zip")) && !(format.equals("gzip")))
				throw new InvalidZipFormatException("Wrong zip format!");
			
			String[] infiles = { this.getFilePath(), this.getCvData().getPhotoPath() };
			String zipfileName = this.getFilePath();
			//*******************************************
			// compress .tex and .jpg file to a .zip file
			if (format.equals("zip")) {
				if (this.getFilePath().endsWith(".tex"))
					zipfileName = zipfileName.substring(0, zipfileName.length() - 4) + ".zip";
				else
					zipfileName = filePath + ".zip";
				
				File zipfile = new File(zipfileName);
				if (!zipfile.exists())
					zipfile.createNewFile();
				//create outputstream
				ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(
						zipfile));
				for (int i = 0; i < infiles.length; i++) {
					//add entries .tex and .jpg
					zos.putNextEntry(new ZipEntry(infiles[i]));
					//define inputstream
					FileInputStream fis = new FileInputStream(infiles[i]);
					int read = fis.read();
					while (read != -1) {//write input to .zip file
						zos.write(read);
						read = fis.read();
					}
					fis.close();
				}
				zos.close();
			//************************
			//compress to .gzip file
			} else if (format.equals("gzip")) {
				if (this.getFilePath().endsWith(".tex"))
					zipfileName = zipfileName.substring(0, zipfileName.length() - 4)
							+ ".gz";
				else
					zipfileName = filePath + ".gz";

				File zipfile = new File(zipfileName);
				if (!zipfile.exists())
					zipfile.createNewFile();
				//create outputstream
				GZIPOutputStream zos = new GZIPOutputStream(
						new FileOutputStream(zipfile));
				for (int i = 0; i < infiles.length; i++) {
					//define input stream
					FileInputStream fis = new FileInputStream(infiles[i]);
					int read = fis.read();
					while (read != -1) {//write to .gzip file
						zos.write(read);
						read = fis.read();
					}
					fis.close();
				}
				zos.close();
			}
			//throw Exceptions
		} catch (IOException e) {
			System.out.println("IO - Fehler: " + e.getMessage());
			return;
		}catch (Exception ex){
			System.out.println("Error: " + ex.getMessage());
			return;
		}
	}
}